package la.bbs558.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import la.bbs558.entity.HotWord;
import la.bbs558.entity.vo.SearchHistoryVO;
import la.bbs558.entity.enums.RedisKey;
import la.bbs558.entity.params.PostSearchPage;
import la.bbs558.entity.params.UserSearchPage;
import la.bbs558.service.BbsPostService;
import la.bbs558.service.HotWordService;
import la.bbs558.service.SearchService;
import la.bbs558.service.UserService;
import la.bbs558.util.Objs;
import la.bbs558.util.Strings;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * @author: Shanks
 * @date: 2023/12/16 22:32
 * @desc:
 */
@Service
@RequiredArgsConstructor
public class SearchServiceImpl implements SearchService {

    // 搜索热度基数
    private static final String searchCountBase = "1";
    // 热词热度基数
    private static final String hotValueBase = "10";

    // 最大搜索词的长度
    private static final long maxSearchWordLength = 19L;

    private final BbsPostService bbsPostService;

    private final UserService userService;

    private final HotWordService hotWordService;

    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void searchPost(PostSearchPage searchPage) {
        Objs.isTrue(Strings.isNotEmpty(searchPage.getKeywords()), "搜索关键字不能为空");
        HotWord hotWord = hotWordService.lambdaQuery()
                .select(HotWord::getId)
                .eq(HotWord::getName, searchPage.getKeywords()).one();
        if (hotWord != null) {
            // 如果热词存在，则更新搜索次数和热度
            hotWordService.lambdaUpdate()
                    .setSql("search_times = search_times + " + searchCountBase)
                    .setSql("hot_value = hot_value + " + hotValueBase)
                    .eq(HotWord::getId, hotWord.getId()).update();
        } else {
            // 如果热词不存在，则新增热词
            hotWordService.save(new HotWord()
                    .setName(searchPage.getKeywords()));
        }
        if (StpUtil.isLogin()) {
            // 将搜索关键字放入redis，用于搜索提示
            long loginId = StpUtil.getLoginIdAsLong();
            String redisKey = RedisKey.SEARCH.getKey() + loginId;
            ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
            Long size = zSetOperations.size(redisKey);
            size = size == null ? 0 : size;
            if (size > maxSearchWordLength) {
                // 将元素限制在maxSearchWordLength个
                zSetOperations.removeRange(redisKey, 0, size - maxSearchWordLength - 1);
            }
            zSetOperations.add(redisKey, searchPage.getKeywords(), System.currentTimeMillis());
        }
        bbsPostService.searchPostByPage(searchPage);
    }

    @Override
    public void searchUser(UserSearchPage searchPage) {
        Objs.isTrue(Strings.isNotEmpty(searchPage.getKeywords()), "搜索关键字不能为空");
        userService.searchUserByPage(searchPage);
    }

    @Override
    public List<HotWord> searchHotList() {
        return hotWordService.lambdaQuery()
                .select(HotWord::getId, HotWord::getName)
                .orderByDesc(HotWord::getHotValue)
                .last("limit 10")
                .list();
    }

    @Override
    public List<SearchHistoryVO> searchHistoryList() {
        String redisKey = RedisKey.SEARCH.getKey() + StpUtil.getLoginId();
        Set<ZSetOperations.TypedTuple<Object>> objects = redisTemplate.opsForZSet().reverseRangeWithScores(redisKey, 0, -1);
        List<SearchHistoryVO> list = new ArrayList<>(20);
        if (Objs.isInvalid(objects)) {
            return list;
        }
        for (ZSetOperations.TypedTuple<Object> tuple : objects) {
            Object value = tuple.getValue();
            Double score = tuple.getScore();
            list.add(new SearchHistoryVO()
                    .setKeywords(value.toString())
                    .setCreateTime(new Date((score.longValue()))));
        }
        return list;
    }

    @Override
    public void clearHistory() {
        String redisKey = RedisKey.SEARCH.getKey() + StpUtil.getLoginId();
        redisTemplate.delete(redisKey);
    }
}
